Skip to content

Conversation

@Turbo87
Copy link
Member

@Turbo87 Turbo87 commented Nov 20, 2025

This PR implements the backend part of #12361.

It adds a new trustpub_only column to the crates table, exposes the field in the API responses, adjusts the publish endpoint to check it, and implements a new PATCH /api/v1/crates/{name} endpoint to toggle it.

The UI implementation will follow in a dedicated PR.

@Turbo87 Turbo87 added the C-enhancement ✨ Category: Adding new behavior or a change to the way an existing feature works label Nov 20, 2025
@Turbo87 Turbo87 requested a review from a team November 20, 2025 15:35
@Turbo87 Turbo87 force-pushed the trustpub-only branch 2 times, most recently from 4c4deb5 to 1b5fb23 Compare November 20, 2025 15:44
Copy link
Contributor

@LawnGnome LawnGnome left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. One or two comments, but the implementation looks good!

&& matches!(auth, AuthType::Regular(_))
{
return Err(forbidden(
"You tried to publish with an API token but this crate requires trusted publishing.",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think people may be confused by "API token" as a term of art, so how about:

Suggested change
"You tried to publish with an API token but this crate requires trusted publishing.",
"New versions of this crate can only be published using Trusted Publishing.",

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm... I specifically wanted to include the "API token" term to make it more obvious to the user what the problem is. I'm not sure what exactly you mean by "term of art". Do you think that crate owners that try to publish something are not aware of what the term "API token" means? 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think that crate owners that try to publish something are not aware of what the term "API token" means?

Honestly, yes, especially in a scenario where this might be an error message they get some time after they configured their crate to require TP. I could see a crate owner — particularly one that's stressed because they're trying to publish a fix for a vulnerability or critical bug — throwing their hands up and wondering how a TP token differs from an API token.

I don't mind including it somewhere in there in a second sentence, but I think we need to frontload the actionable part, which is that Trusted Publishing is required.

{%- endif %}

{% if trustpub_only -%}
This crate can now ONLY be published via Trusted Publishing. Publishing with API tokens has been disabled.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(to follow on from my previous comment, I don't have the same concern over the use of "API token" here because the user is likely to have immediate context, since they or another owner were just thinking about trusted publishing)

Copy link
Contributor

@eth3lbert eth3lbert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a minor nit. Overall LGTM! thanks!

Comment on lines +93 to +129
// Update trustpub_only if provided
if let Some(trustpub_only) = body.trustpub_only {
diesel::update(crates::table)
.filter(crates::id.eq(krate.id))
.set(crates::trustpub_only.eq(trustpub_only))
.execute(conn)
.await?;

// Audit log the setting change
info!(
target: "audit",
action = "trustpub_only_change",
krate.name = %krate.name,
network.client.ip = %**real_ip,
usr.id = user.id,
usr.name = %user.gh_login,
"User {} set trustpub_only={trustpub_only} for crate {}",
user.gh_login,
krate.name
);

// Send email notifications to all crate owners
for (_, gh_login, email_address, email_verified) in &user_owners {
if *email_verified {
let email = TrustpubOnlyChangedEmail {
recipient: gh_login,
auth_user: user,
krate,
trustpub_only,
};

if let Err(err) = email.send(app, email_address).await {
warn!("Failed to send trustpub_only notification to {email_address}: {err}");
}
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a blocker, but I'm wondering if we should check whether the given value is different from the existing value since we are sending a notification?


Nevertheless, I especially love the introduced notification mechanism here. Without it, a malicious user with a leaked API token could change the setting and publish a malicious version, which the crate owner might not notice. But with this mechanism, the crate owner has a chance to rotate the token and make amends!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-backend ⚙️ C-enhancement ✨ Category: Adding new behavior or a change to the way an existing feature works

Projects

Status: For next meeting

Development

Successfully merging this pull request may close these issues.

4 participants